/*
XPTracker - Alternative Agile Tracking Tool
Copyright (C) 2006-2009  Stephen Starkey

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
*/
package org.xptracker.web;

import com.google.inject.Inject;
import net.sourceforge.stripes.action.*;
import net.sourceforge.stripes.controller.LifecycleStage;
import net.sourceforge.stripes.security.action.Secure;
import net.sourceforge.stripes.validation.SimpleError;
import net.sourceforge.stripes.validation.ValidationErrors;
import net.sourceforge.stripes.validation.ValidationMethod;
import org.apache.commons.lang.StringUtils;
import org.xptracker.domain.Role;
import org.xptracker.domain.User;
import org.xptracker.persistence.PersistenceManager;
import org.xptracker.services.EmailService;
import org.xptracker.services.auth.Authenticator;
import org.xptracker.util.DicewareGenerator;

import java.util.List;

@UrlBinding("/Users.action")
public class UsersActionBean extends StandardActionBean {
  private List<User> users;

  @Inject
  private DicewareGenerator generator;

  @Inject
  @SuppressWarnings({"UnusedDeclaration"})
  private Authenticator authenticator;

  @Inject
  @SuppressWarnings({"UnusedDeclaration"})
  private EmailService emailer;

  private User loggedIn;
  private String role;
  private boolean enabled;
  public String username;
  public String uuid;

  public String getUuid() {
    return uuid;
  }

  public void setUuid(String uuid) {
    this.uuid = uuid;
  }

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public Authenticator getAuthenticator() {
    return authenticator;
  }

  public List<User> getUsers() {
    return users;
  }

  public void setUsers(List<User> users) {
    this.users = users;
  }

  public User getLoggedIn() {
    return loggedIn;
  }

  public void setLoggedIn(User loggedIn) {
    this.loggedIn = loggedIn;
  }

  public void setRole(String role) {
    this.role = role;
  }

  public void setEnabled(boolean enabled) {
    this.enabled = enabled;
  }

  @DefaultHandler
  public Resolution listUsers() {
    return new ForwardResolution("/admin/users.jsp");
  }

  @ValidationMethod(on = "Add")
  public void validateUser(ValidationErrors errors) throws Exception {
    if (StringUtils.isBlank(username)) {
      errors.add("username", new SimpleError("Blank usernames are not allowed."));
    } else {
      User u = domain.loadUser(username);
      if (u != null) {
        errors.add("username", new SimpleError("User ''{1}'' already exists.", username));
      }
    }
  }

  @Secure(roles = "admin")
  @HandlesEvent("Add")
  public Resolution addUser() {
    return persistenceManager.inTransaction(new PersistenceManager.UpdateHandler<Resolution>() {
      public Resolution doUpdate(PersistenceManager.UpdateReceiver receiver) {
        User user = new User(username);
        user.addRole(Role.editor);
        receiver.insert(user);
        String newPassword = updatePassword(user, receiver);
        getContext().getMessages().add(new SimpleMessage("User ''{0}'' added with password ''{1}''.", username,
            newPassword));
        return new RedirectResolution(UsersActionBean.class).flash(UsersActionBean.this);
      }
    });
  }

  private String updatePassword(User user, PersistenceManager.UpdateReceiver receiver) {
    String newPassword = newPassword();
    user.setPassword(newPassword);
    receiver.update(user);
    return newPassword;
  }

  private String newPassword() {
    return generator.generate();
  }

  @Secure(roles = "admin")
  @HandlesEvent("ResetPassword")
  public Resolution resetPassword() {
    if (authenticator.isPasswordManager()) {
      persistenceManager.inTransaction(new PersistenceManager.UpdateHandler<Object>() {
        public Object doUpdate(PersistenceManager.UpdateReceiver receiver) {
          User u = loadEntity(User.class, uuid);
          String newPassword = updatePassword(u, receiver);
          getContext().getMessages().add(new SimpleMessage("Password for User ''{0}'' changed to ''{1}''.",
              u.getUsername(), newPassword));
          if (StringUtils.isNotBlank(u.getEmail())) {
            emailer.sendEmail(u, u, "XPTracker Password Reset", newPassword);
            getContext().getMessages().add(new SimpleMessage("Email sent."));
          } else {
            getContext().getValidationErrors().addGlobalError(new SimpleError("User had no email address.  Email NOT Sent."));
          }
          return null;
        }
      });
    }
    return new RedirectResolution(UsersActionBean.class).flash(this);
  }

  @Secure(roles = "admin")
  @HandlesEvent("AddRole")
  public Resolution addRole() {
    return modifyUser(new UserModifier() {
      public String modifyUser(User user) {
        user.addRole(Role.valueOf(role));
        return "Added role ''" + role + "'' to user ''" + user.getUsername() + "''";
      }
    });
  }

  @Secure(roles = "admin")
  @HandlesEvent("RemoveRole")
  public Resolution removeRole() {
    return modifyUser(new UserModifier() {
      public String modifyUser(User user) {
        user.removeRole(Role.valueOf(role));
        return "Removed role ''" + role + "'' from user ''" + user.getUsername() + "''";
      }
    });
  }

  @Secure(roles="admin")
  @HandlesEvent("SetEnabled")
  public Resolution setEnabled() {
    return modifyUser(new UserModifier() {
      public String modifyUser(User user) {
        user.setEnabled(enabled);
        return "User ''" + user.getUsername() + "'' " + (enabled ? "enabled" : "disabled");
      }
    });
  }

  private Resolution modifyUser(final UserModifier modifier) {
    return persistenceManager.inTransaction(new PersistenceManager.UpdateHandler<Resolution>() {
      public Resolution doUpdate(PersistenceManager.UpdateReceiver receiver) {
        User u = loadEntity(User.class, uuid);
        addMessage(modifier.modifyUser(u));
        receiver.update(u);
        return new RedirectResolution(UsersActionBean.class);
      }
    });
  }

  private static interface UserModifier {
    String modifyUser(User user);
  }

  @Before(stages=LifecycleStage.BindingAndValidation)
  public void provideUsers() {
    this.loggedIn = getCurrentUser();
    this.users = domain.listUsers(loggedIn.hasRole(Role.admin));
  }
}
